package com.talkweb.demo.search;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @description: 描述
 * @className：Test
 * @author: lj
 * @date: 2024/10/8 17:47
 */
public class AppBak {
    final static Logger log = LoggerFactory.getLogger(AppBak.class);

    public static void main(String[] args) {
        FileCacheUtil.init("cache.txt");
        search();
    }

    private static void search() {
        Scanner scanner = new Scanner(System.in);
        String source = null;
        while (null == source) {
            log.info("搜索目录：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                File file = new File(input);
                if (file.exists()) {
                    source = input;
                }
            }
        }

        Boolean cascade = null;
        while (null == cascade) {
            log.info("是否递归搜索子目录(y/n)[n]：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if ("y".equalsIgnoreCase(input)) {
                    cascade = Boolean.TRUE;
                } else if ("n".equalsIgnoreCase(input)) {
                    cascade = Boolean.FALSE;
                }
            } else {
                cascade = Boolean.FALSE;
            }
        }

        Long max = null;
        while (null == max) {
            log.info("文件最大限制（M）[100]：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if (NumberUtil.isNumber(input)) {
                    max = Long.valueOf(input) * 1024 * 1024;
                }
            } else {
                max = 100L * 1024 * 1024;
            }
        }

        String target = null;
        while (null == target) {
            log.info("命中后移动到目录：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if (input.equalsIgnoreCase(source)) {
                    log.info("！！移动到的目录不能与搜索目录相同！！");
                } else {
                    target = input;
                }
            }
        }

        String keyword = null;
        while (null == keyword) {
            log.info("搜索关键字：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                keyword = input;
            }
        }

        Integer poolSize = null;
        while (null == poolSize) {
            log.info("并发线程数[4]：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if (NumberUtil.isNumber(input)) {
                    Integer i = Integer.valueOf(input);
                    if (i > 999) {
                        log.info("！！最多999个线程！！");
                    }
                    poolSize = i;
                }
            } else {
                poolSize = 4;
            }
        }
        ExecutorService executor = Executors.newFixedThreadPool(poolSize);

        log.info("=======================================================");
        log.info("搜索目录：" + source);
        log.info("是否递归搜索子目录：" + cascade);
        log.info("文件最大限制（M）：" + formatSize(max));
        log.info("命中后移动到目录：" + target);
        log.info("异常时移动到目录：" + target + File.separator + "error");
        log.info("搜索关键字：" + keyword);
        log.info("并发线程数：" + poolSize);
        log.info("=======================================================");
        log.info("输入[y]确认开始，其他任意键退出：");
        String input = scanner.nextLine();


        scanner.close();
        if (!"y".equalsIgnoreCase(input)) {
            System.exit(0);
        }

        if (cascade) {
            runThread(executor, source, null, cascade, max, target, keyword);
        } else {
            File sourceFile = new File(source);
            if (sourceFile.isFile()) {
                runThread(executor, source, null, cascade, max, target, keyword);
            } else {
                File[] files = sourceFile.listFiles();
                for (int i = 0; i < files.length; i++) {
                    File file = files[i];
                    if (file.isFile()) {
                        runThread(executor, source, file, cascade, max, target, keyword);
                    }
                }
            }
        }
        while (!executor.isTerminated()) {
            System.out.println("==========================如果所有任务已完成，请手动结束进程~~~");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static void runThread(ExecutorService executor, String source, File sourceFile, Boolean cascade, Long max, String target, String keyword) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                begin(executor, source, sourceFile, cascade, max, target, keyword);
            }
        });
    }

    public static void begin(ExecutorService executor, String source, File sourceFile, Boolean cascade, Long max, String target, String keyword) {
        if (null == sourceFile) {
            sourceFile = new File(source);
        }
        String sourceFileAbsolutePath = sourceFile.getAbsolutePath();
        if (!sourceFile.exists()) {
            log.error("指定的源文件不存在:" + sourceFileAbsolutePath);
            return;
        }
        if (null == cascade) {
            cascade = Boolean.FALSE;
        }
        if (sourceFile.isFile()) {
            if (FileCacheUtil.exists(sourceFileAbsolutePath)) {
                log.info("==== 已处理过的文件，不重复处理:" + sourceFileAbsolutePath + " ====");
                return;
            }
            long length = sourceFile.length();
            if (length > max) {
                log.error("==== 文件大小超过限制，不处理:" + sourceFileAbsolutePath + " size:" + formatSize(length) + " ====");
                return;
            } else {
                log.info("开始查找:" + sourceFileAbsolutePath + " size:" + formatSize(length));
            }
            String ext = sourceFile.getName();
            boolean exists = false;
            if (ext.endsWith(".docx") || ext.endsWith(".doc")) {
                exists = WordKeywordSearch(sourceFile, target, keyword);
            } else if (ext.endsWith(".pdf")) {
                exists = PDFKeywordSearch(sourceFile, target, keyword);
            } else {
                log.error("不支持的文件类型：" + sourceFileAbsolutePath);
            }
            if (exists) {
                String targetPath = sourceFileAbsolutePath.replace(source, target);
                File targetFile = new File(targetPath);
                if (targetFile.exists()) {
                    log.error("目标路径已存在，不做任何操作：" + targetPath);
                    return;
                }
                targetFile.getParentFile().mkdirs();
                FileUtil.move(sourceFile, targetFile, false);
            }
            FileCacheUtil.cache(sourceFileAbsolutePath);
        } else if (sourceFile.isDirectory()) {
            if (!cascade) {
                return;
            }
            log.info("开始扫描:" + sourceFileAbsolutePath);
            File[] files = sourceFile.listFiles();
            for (int i = 0; i < files.length; i++) {
                runThread(executor, source, files[i], cascade, max, target, keyword);
            }
        } else {
            log.error("未识别：" + sourceFileAbsolutePath);
        }
    }

    public static boolean WordKeywordSearch(File sourceFile, String target, String keyword) {


        try (FileInputStream fis = new FileInputStream(sourceFile);
             XWPFDocument document = new XWPFDocument(fis)) {
            StringBuffer sb = new StringBuffer();
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                sb.append(paragraph.getText().trim());
            }
            if (sb.toString().contains(keyword)) {
                log.info(" ====关键字 '" + keyword + "' 存在于文档中：" + sourceFile.getAbsolutePath() + " ====");
                return true;
            } else {
                log.info(" ====关键字 '" + keyword + "' 不存在于文档中：" + sourceFile.getAbsolutePath() + " ====");
                return false;
            }

        } catch (IOException e) {
            e.printStackTrace();
            File targetFile = new File(target + File.separator + "error" + File.separator + sourceFile.getName());
            FileUtil.move(sourceFile, targetFile, false);
        }
        return false;
    }

    public static boolean PDFKeywordSearch(File sourceFile, String target, String keyword) {
        try (PDDocument document = PDDocument.load(sourceFile)) {
            PDFTextStripper stripper = new PDFTextStripper();
            String text = stripper.getText(document);
            if (text.contains(keyword)) {
                log.info(" ====关键字 '" + keyword + "' 存在于文档中：" + sourceFile.getAbsolutePath() + " ====");
                return true;
            } else {
                log.info(" ====关键字 '" + keyword + "' 不存在于文档中：" + sourceFile.getAbsolutePath() + " ====");
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            File targetFile = new File(target + File.separator + "error" + File.separator + sourceFile.getName());
            FileUtil.move(sourceFile, targetFile, false);
        }
        return false;
    }

    private static String formatSize(long size) {
        if (size < 1024) {
            return size + "B";
        } else if (size < 1024 * 1024) {
            return size / 1024 + "K";
        } else {
            return size / 1024 / 1024 + "M";
        }
    }
}
